package com.jeeplus.modules.guide.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeeplus.common.utils.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.KeyFactory;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * 服务集成平台
 */
public class GaoXinFwjcptTest {


    /**
     * 可引入的pom（仅供参考）
     <dependency>
         <groupId>org.bouncycastle</groupId>
         <artifactId>bcprov-jdk15to18</artifactId>
         <version>1.69</version>
     </dependency>
     <dependency>
         <groupId>org.apache.httpcomponents</groupId>
         <artifactId>httpclient</artifactId>
     </dependency>
     <dependency>
         <groupId>com.alibaba</groupId>
         <artifactId>fastjson</artifactId>
     </dependency>

     // 不重要
     <dependency>
         <groupId>commons-logging</groupId>
         <artifactId>commons-logging</artifactId>
         <version>1.2</version>
     </dependency>
     */

    /**
     * 成功标识
     */
    private static final String SUCCESS = "1";

    /**
     * 服务地址
     */
    private String url;

    /**
     * 接入标识（同一套系统可使用一个接入标识）
     */
    private String accessKey;

    /**
     * 接入密钥（和接入标识一一对应的私钥，用于数据签名验签）
     */
    private String privateKey;

    /**
     * 数据加密密钥（用于对业务数据进行加解密）
     */
    private String dataSecret;

    /**
     * 数据格式（默认json）
     */
    private String dateFormat = "json";

    /**
     * 接口版本号（默认1.0）
     */
    private String version = "1.0";

    /**
     * 渠道标识（唯一标识一个渠道）
     */
    private String appid;

    /**
     * 构造方法
     *
     * @param url        服务地址
     * @param accessKey  接入标识
     * @param privateKey 接入密钥
     */
    public GaoXinFwjcptTest(String url, String accessKey, String privateKey) {
        this.url = url;
        this.accessKey = accessKey;
        this.privateKey = privateKey;
    }

    /**
     * 构造方法
     *
     * @param url        服务地址
     * @param accessKey  接入标识
     * @param privateKey 接入密钥
     * @param dataSecret 数据加密密钥
     * @param appid      渠道标识
     */
    public GaoXinFwjcptTest(String url, String accessKey, String privateKey, String dataSecret, String appid) {
        this.url = url;
        this.accessKey = accessKey;
        this.privateKey = privateKey;
        this.appid = appid;
        this.dataSecret = dataSecret;
    }

    static{
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 发送请求
     *
     * @param serviceSign 服务标识
     * @param content     内容
     * @return 响应内容
     * @throws Exception 调用异常
     */
    public String sendRequest(String serviceSign, String content) throws Exception {
        // 请求流水号
        String requestId = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 20);
        // 时间戳
        String timestamp = String.valueOf(System.currentTimeMillis());
        // 获取签名字符串
        StringBuilder builder = new StringBuilder();
        builder.append("access_key=" + accessKey + "&")
                .append("biz_content=" + content + "&")
                .append("format=" + dateFormat + "&")
                .append("request_id=" + requestId + "&")
                .append("timestamp=" + timestamp + "&")
                .append("version=" + version);
        // 计算签名
        String sign = EncryptUtils.sm2Sign(privateKey, builder.toString());
        // 构建http请求
        HttpPost httpPost = new HttpPost(url + serviceSign);
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        //设置不使用长连接
        httpPost.setHeader("Connection", "close");
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(8000)
                .setSocketTimeout(8000).setConnectTimeout(30000).build();
        httpPost.setConfig(requestConfig);
        // 封装数据
        List<NameValuePair> params = new ArrayList<>();
        params.add(new BasicNameValuePair("access_key", accessKey));
        params.add(new BasicNameValuePair("biz_content", content));
        params.add(new BasicNameValuePair("format", dateFormat));
        params.add(new BasicNameValuePair("request_id", requestId));
        params.add(new BasicNameValuePair("timestamp", timestamp));
        params.add(new BasicNameValuePair("version", version));
        params.add(new BasicNameValuePair("sign", sign));
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, StandardCharsets.UTF_8);
        httpPost.setEntity(entity);
        // 发送POST请求, 并获取响应
        HttpResponse response = null;
        try {
            response = httpClient.execute(httpPost);
        } catch (Exception e) {
            throw new Exception("网络错误: " + e.getMessage());
        }
        // 检验返回码
        int statusCode = response.getStatusLine().getStatusCode();
        if (HttpStatus.SC_OK != response.getStatusLine().getStatusCode()) {
            throw new Exception("错误响应码：" + statusCode);
        }
        // 返回响应字符串
        String responseContent = EntityUtils.toString(response.getEntity());
        JSONObject jsonObject = JSON.parseObject(responseContent);
        String code = jsonObject.getString("code");
        String msg = jsonObject.getString("msg");
        String subCode = jsonObject.getString("sub_code");
        String subMsg = jsonObject.getString("sub_msg");
        if (!SUCCESS.equals(code)) {
            throw new Exception("一卡通服务调用错误:" + msg + "-" + subMsg);
        }
        return jsonObject.getString("biz_data");
    }

    /**
     * 执行调用
     *
     * @param serviceSign     服务标识
     * @param requestNo       请求业务流水号，唯一标识本次服务调用
     * @param serviceItemCode 服务事项编码，表示本次服务所使用的服务事项
     * @param data            业务数据
     * @return 响应数据字符串
     */
    public String invoke(String serviceSign, String requestNo, String serviceItemCode, Object data) throws Exception {
        return invoke(serviceSign, this.appid, this.dataSecret, requestNo, serviceItemCode, data);
    }

    /**
     * 执行调用
     *
     * @param serviceSign     服务标识
     * @param appid           渠道标识
     * @param dataSecret      数据加密密钥
     * @param requestNo       请求业务流水号，唯一标识本次服务调用
     * @param serviceItemCode 服务事项编码，表示本次服务所使用的服务事项
     * @param data            业务数据
     * @return 响应数据字符串
     */
    public String invoke(String serviceSign, String appid, String dataSecret, String requestNo, String serviceItemCode, Object data) throws Exception {
        Map<String, Object> params = new HashMap<>();
        // 渠道标识、业务流水号，服务事项编码
        params.put("appid", appid);
        params.put("aaz001", requestNo);
        params.put("serviceCode", serviceItemCode);
        // 序列化请求数据
        String dataString = JSON.toJSONString(data);
        if (dataString != null) {
            // 请求数据不为空，则进行加密处理
            params.put("data", EncryptUtils.sm4Encrypt(dataSecret, dataString));
        }
        // 序列号参数并发送请求
        String paramString = JSON.toJSONString(params);
        String result = sendRequest(serviceSign, paramString);
        // 反序列化
        JSONObject responseObj = JSON.parseObject(result);
        // 校验，响应渠道标识、响应流水号、响应状态
        if (!appid.equalsIgnoreCase(responseObj.getString("appid"))) {
            throw new Exception("服务调用响应渠道标识不匹配");
        }
        if (!requestNo.equalsIgnoreCase(responseObj.getString("aaz001"))) {
            throw new Exception("服务调用响应请求业务流水号不匹配");
        }
        if (!"1".equalsIgnoreCase(responseObj.getString("status"))) {
            throw new Exception("服务调用失败: " + responseObj.getString("message"));
        }
        // 获取响应数据
        String responseDataText = responseObj.getString("data");
        if (responseDataText == null) {
            return null;
        }
        // 响应数据不为空，则进行解密处理
        return EncryptUtils.sm4Decrypt(dataSecret, responseDataText);
    }

    /**
     * 电子社保卡调用 解析电子社保用户信息
     */
    public static String toSHBZK(String qrCode,String Authcode,String url,String accessKey,String privateKey,String appid,String appSecret)  {
        //硬件传值
        JSONObject data = new JSONObject();
        //电子二维码
        data.put("qrCode", qrCode);
        //硬件系统接入编号
        data.put("aaz400", "51010382AK");
        //硬件系统接入秘钥
        data.put("sm4key", "fdea1dc2de2e4c88b7ea3b947c0ded9a");
        GaoXinFwjcptTest invoker = new GaoXinFwjcptTest(url, accessKey, privateKey);
        String requestNo = String.valueOf(System.currentTimeMillis());
        String result;
        try{
            //获取用户电子社保信息 无认证码
            if(StringUtils.isBlank(Authcode)){
                //统一社会信用代码
                data.put("orgCode", "11510109MB143832XC");
                //组织机构名称
                data.put("orgName", "成都高新技术产业开发区智慧城市运行局，-" +
                        "");
                //编码
                data.put("regionCode", "610095");
                return SMAuthcode(invoker, appid, requestNo, appSecret, data);
            }else{
                //电子二维码解析内容 Authcode认证码
                data.put("rzm", Authcode);
                return SMLogin(invoker, appid, requestNo, appSecret,data);
            }
        }catch (Exception e){
            e.printStackTrace();
            return e.getMessage();
        }
    }


    public static String toSHBZKPZ(String qrCode,String Authcode,String url,String accessKey,String privateKey,String appid,String appSecret)  {
        String regex = "\\|";
        String[] list = qrCode.split(regex);
        //硬件传值
        JSONObject data = new JSONObject();
        //姓名
        data.put("aac003", list[4]);
        data.put("zdbm", list[10]);
        data.put("aaz501", list[3]);
        data.put("aab034", list[0]);
        //社会保障号
        data.put("aac002", list[1]);
        //社会保障卡号
        data.put("aaz500", list[2]);
        //电子二维码
        //data.put("qrCode", qrCode);
        //硬件系统接入编号
        data.put("aaz400", "51010382AK");
        //硬件系统接入秘钥
        data.put("sm4key", "fdea1dc2de2e4c88b7ea3b947c0ded9a");
        data.put("rzm", Authcode);
        GaoXinFwjcptTest invoker = new GaoXinFwjcptTest(url, accessKey, privateKey);
        SimpleDateFormat sdf =new SimpleDateFormat("yyyyMMddHHmmss");
        String requestNo = sdf.format(new Date());
        try{
            return SMLoginPZ(invoker, appid, requestNo, appSecret,data);
        }catch (Exception e){
            e.printStackTrace();
            return e.getMessage();
        }
    }


    private static String SMLoginPZ(GaoXinFwjcptTest invoker, String appid, String requestNo, String appSecret,JSONObject data) throws Exception {
        //接口调用标识：
        //社保卡卡鉴权：99999900001
        //二维码解码服务：99999800002 （电子二维码解析调用）
        //主扫登录——二维码生成接口：99999800003
        //主扫登录——授权结果查询服务99999800004

        //主扫登录——二维码生成接口
        final String serviceSign = "99999900001";
        //服务事项
        final String serviceItemCode = "00500100301";
        return invoker.invoke(serviceSign, appid, appSecret, requestNo, serviceItemCode, data);
    }

    /**
     * 电子社保卡调用 获取认证码
     */
    private static String SMAuthcode(GaoXinFwjcptTest invoker, String appid, String requestNo, String appSecret,JSONObject data) throws Exception {
        //获取电子二维码解析调用 无认证码
        final String serviceSign = "99999800009";
        //服务事项
        final String serviceItemCode = "00500100301";
        return invoker.invoke(serviceSign, appid, appSecret, requestNo, serviceItemCode, data);
    }

    private static String SMLogin(GaoXinFwjcptTest invoker, String appid, String requestNo, String appSecret,JSONObject data) throws Exception {
        //接口调用标识：
        //社保卡卡鉴权：99999900001
        //二维码解码服务：99999800002 （电子二维码解析调用）
        //主扫登录——二维码生成接口：99999800003
        //主扫登录——授权结果查询服务99999800004

        //主扫登录——二维码生成接口
        final String serviceSign = "99999800002";
        //服务事项
        final String serviceItemCode = "00500100301";
        return invoker.invoke(serviceSign, appid, appSecret, requestNo, serviceItemCode, data);
    }



    /**
     * 加密工具类
     */
    private static abstract class EncryptUtils {

        /**
         * 字符串转私钥对象
         *
         * @param privateKey 私钥文本
         * @return 私钥对象
         */
        public static ECPrivateKeyParameters toPrivateKey(String privateKey) throws Exception {
            byte[] encodedKey = Base64.getDecoder().decode(privateKey);
            PKCS8EncodedKeySpec peks = new PKCS8EncodedKeySpec(encodedKey);
            KeyFactory kf = KeyFactory.getInstance("EC");
            BCECPrivateKey ecPriKey = (BCECPrivateKey) kf.generatePrivate(peks);
            ECParameterSpec parameterSpec = ecPriKey.getParameters();
            ECDomainParameters domainParameters = new ECDomainParameters(parameterSpec.getCurve(), parameterSpec.getG(), parameterSpec.getN(), parameterSpec.getH());
            return new ECPrivateKeyParameters(ecPriKey.getD(), domainParameters);
        }

        /**
         * sm2签名
         *
         * @param privateKey 私钥文本
         * @param content    内容
         * @return 签名串
         */
        public static String sm2Sign(String privateKey, String content) throws Exception {
            ECPrivateKeyParameters priKeyParameters = toPrivateKey(privateKey);
            SM2Signer signer = new SM2Signer();
            ParametersWithRandom pwr = new ParametersWithRandom(priKeyParameters, new SecureRandom());
            signer.init(true, pwr);
            byte[] data = content.getBytes(StandardCharsets.UTF_8);
            signer.update(data, 0, data.length);
            return Base64.getEncoder().encodeToString(signer.generateSignature());
        }

        /**
         * SM4加密
         *
         * @param key     密钥
         * @param content 内容
         * @return 加密文本
         */
        public static Object sm4Encrypt(String key, String content) throws Exception {
            if (content == null) {
                return null;
            }
            Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS7Padding");
            byte[] byteContent = content.getBytes(StandardCharsets.UTF_8);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "SM4"), (AlgorithmParameters) null);
            byte[] encryptByte = cipher.doFinal(byteContent);
            return new String(Base64.getEncoder().encode(encryptByte), StandardCharsets.UTF_8);
        }

        /**
         * SM4解密
         *
         * @param key     数据密钥
         * @param content 内容
         * @return 解密文本
         */
        public static String sm4Decrypt(String key, String content) throws Exception {
            if (content == null) {
                return null;
            }
            Cipher cipher = Cipher.getInstance("SM4/ECB/PKCS7Padding");
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "SM4"), (AlgorithmParameters) null);
            byte[] encryptByte = cipher.doFinal(Base64.getDecoder().decode(content));
            return new String(encryptByte, StandardCharsets.UTF_8);
        }
    }
}
